// Copyrights by Kenneth Lee, 2022. All Rights Reserved
#include "ana_val.hpp"

AnaVal::AnaVal(int i) {
	numer = i;
	demom = 1;
	state = ST_VAL;
}

AnaVal::AnaVal(int n, int d) {
	numer = n;
	demom = d;
	state = ST_VAL;
	simplify();
}

AnaVal::AnaVal() {
	state = ST_NON;
	numer = demom = 0;
}

AnaVal::AnaVal(const char *s) {
	*this = AnaVal(atoi(s));
}

void AnaVal::simplify(void) {
	if (state != ValIf::ST_VAL) {
		numer = demom = 0;
		return;
	}

	if (demom == 0) {
		state = ValIf::ST_INV;
		numer = 0;
		return;
	}

	if (demom < 0) {
		numer = -numer;
		demom = -demom;
	}

	if (numer == 0)
		demom = 1;

	// reduction
	for (int i = 2, m = min(numer, demom); i <= m;) {
		if ((numer % i) == 0 && (demom % i) == 0) {
			numer /= i;
			demom /= i;
		} else
			i++;
	}
}

bool operator==(const AnaVal &l, const AnaVal &r) {
	if (!((ValIf)l).state_compare((ValIf &)r))
		return false;

	return (l.numer == r.numer ) && (l.demom == r.demom);
}

bool operator==(const AnaVal &l, const int &r) {
	return l == AnaVal(r);
}

ostream & operator<<(ostream & os, const AnaVal & v) {
	if (v.state !=ValIf::ST_VAL)
		((ValIf&)v).show(os);
	else {
		if (v.numer == 0)
			os << "0";
		else if (v.demom == 1)
			os << v.numer;
		else
			os << "(" << v.numer << "/" << v.demom << ")";
	}
	return os;
}

AnaVal operator+(const AnaVal &l, const AnaVal &r) {
	AnaVal ret;
	if (((ValIf&)ret).op_init(l, r)) {
		ret.numer = l.numer * r.demom + r.numer * l.demom;
		ret.demom = l.demom * r.demom;
		ret.simplify();
	}
	return ret;
}

AnaVal operator-(const AnaVal &l, const AnaVal &r) {
	AnaVal ret;
	if (((ValIf&)ret).op_init(l, r)) {
		ret.numer = l.numer * r.demom - r.numer * l.demom;
		ret.demom = l.demom * r.demom;
		ret.simplify();
	}
	return ret;
}

AnaVal operator*(const AnaVal &l, const AnaVal &r) {
	AnaVal ret;
	if (((ValIf&)ret).op_init(l, r)) {
		ret.numer = l.numer * r.numer;
		ret.demom = l.demom * r.demom;
		ret.simplify();
	}
	return ret;
}

AnaVal operator/(const AnaVal &l, const AnaVal &r) {
	AnaVal ret;
	if (((ValIf&)ret).op_init(l, r)) {
		ret.numer = l.numer * r.demom;
		ret.demom = l.demom * r.numer;
		ret.simplify();
	}
	return ret;
}

AnaVal & AnaVal::operator=(const char *s) {
	*this = AnaVal(atoi(s));
	return *this;
}
